package org.NooLab.glue.instances;

 
import org.NooLab.compare.utilities.math.DistanceControlProperties;
import org.NooLab.glue.MessageBoard;
import org.NooLab.glue.components.MessageBoardFactory;
import org.NooLab.glue.components.MessageBoardFactoryProperties;
import org.NooLab.glue.components.pkg.TaskPackage;
import org.NooLab.glue.msgframe.XRegistration;
import org.NooLab.utilities.timing.Age;



/**
 * 
 * the purpose of this Factory is to just provide a simplified interface for
 * a more clean use, which does not need inheritance
 * 
 * It delivers SOURCEs, RECEPTORs and bivalent clients as well!
 * 
 * 
 * 
 */ 
public class ParticipantFactory { // extends MessageBoardParticipant

	public static final int PS_ALIVE     = 1 ;
	public static final int PS_CONNECTED = 20 ;
	
	
	// ----------------------------------------------------
	
	MessageBoardFactoryProperties factoryProperties ;
	ParticipantBaseIntf instance=null;
	Object parent;
	MessageBoardFactory mbf ;
	MessageBoard msgBoardReference;
	
	// MessageBoardParticipant local;
	
	XRegistration xRegistration;
	TaskPackage taskPackage ;
	
	ParticipantsSettings pSettings = new ParticipantsSettings();
	
	ResultReceiverIntf resultsDeliveryStation ;
	TaskReceiverIntf   taskDeliveryStation ;
	
	// ParticipantUpLinkServiceIntf upLinkService;
	SourcesUpLinkService sourcesUpLinkService ;
	ReceptorsUpLinkService receptorsUpLinkService;
	
	int status;
	long taskTTL = 0;
	
	boolean connected=false; 
	
		
	public  ParticipantFactory( MessageBoardFactoryProperties properties, Object parent ){
		//super( properties.getConfigPath(), ParticipantSourceIntf.roleOfInstance.toLowerCase()+ properties.getNameSuffix());
		
		factoryProperties = properties;
		this.parent = parent;
		
		
		init();
		
	}
	
	public  ParticipantFactory( String propertiesFile , Object parent ){
		
		this.parent = parent;
		factoryProperties = new MessageBoardFactoryProperties() ;
		
		factoryProperties.setAdditionalConfig(pSettings);   
		
		factoryProperties.loadProperties( propertiesFile );
	}
	
	private void init(){
				
		factoryProperties.setAdditionalConfig(pSettings);   
		setCallOut();
		
		factoryProperties.setParticipantFactory(this) ;
		
		
	}
 
	 
	
	private void setCallOut(){
		if (isSource()){
			resultsDeliveryStation = (ResultReceiverIntf) parent ;
		}
		if (isReceptor()){
			taskDeliveryStation = (TaskReceiverIntf) parent ;
		}		
		
	}
	
	public MessageBoardFactoryProperties getFactoryProperties(){
		return factoryProperties ;
	}
	
	public ParticipantsSettings config(){
		return pSettings;
	}
	
	
	private boolean isSource(){
		boolean hb;
		hb = (factoryProperties.getParticipantsRole().toLowerCase().contains(MessageBoardFactoryProperties.variant_SOURCE)) ||
			 (factoryProperties.getParticipantsRole().toLowerCase().contains(MessageBoardFactoryProperties.variant_BSOURCE)) ;
		return hb;
	}
	private boolean isEmbeddedSource(){
		return  (factoryProperties.getParticipantsRole().toLowerCase().contains(MessageBoardFactoryProperties.variant_BSOURCE)) ;
	}
	
	private boolean isReceptor(){
		return factoryProperties.getParticipantsRole().toLowerCase().contains(MessageBoardFactoryProperties.variant_RECEPTOR) ;
	}
	private boolean isBoard(){
		return factoryProperties.getParticipantsRole().toLowerCase().contains(MessageBoardFactoryProperties.variant_BOARD) ;
	}
	private boolean isBiRS(){
		return factoryProperties.getParticipantsRole().toLowerCase().contains(MessageBoardFactoryProperties.variant_BICLIENT) ;
	}
	
	
	private void transferSettings( MessageBoardParticipant part ){
		// all settings that refer to the super class 
		part.setMessageBoardFactoryProperties( factoryProperties ) ;
		
		part.setParticipantsRole(factoryProperties.participantsRole );
		
		part.setInstanceIsUnique( pSettings.instanceIsUnique );
		
		part.activeReceiptsExchange( pSettings.activeReceiptsExchange ) ;
		
		if ( pSettings.instanceName.length()>0){
			part.setName( pSettings.instanceName ) ;
		}
		
		part.setContentType( pSettings.defaultDocType ) ;
		
		part.collectResults = pSettings.collectResults ;
		
		part.setDelayBetweenConnectRetries( pSettings.delayBetweenConnectRetries );
		part.setNumberOfConnectRetry( pSettings.numberOfConnectRetry ) ;
		part.setDelayProgressionFactor( pSettings.delayProgressionFactor ) ;
		
		part.setMaxDelayBetweenReconnects( pSettings.maxPeriodLengthBetweenReconnects ) ;
	}
	
	/**
	 * this guarantees unique instances, even if the wires broke down. Not Yet Implemented...
	 */
	private void clearRemoteInstance(){
		
		
		if ( pSettings.instanceIsUnique ){
			// this would need to create/connect a non-unique dummy first, 
			// then issue a command to the board to remove upon next unique application
			// identified by UUID name
			// hence, it has to run on the level of the ParticipantFactory !
			
		}
	}
	
	public Object connect(){
		
		Object partClient=null;
		ParticipantSourceIntf srcParticipant ;
		ParticipantReceptorIntf recParticipant ;
		
		if (isSource()){
			 
			clearRemoteInstance();
			
			srcParticipant = (ParticipantSourceIntf)(getInstance()) ; 
			// instance = (ParticipantBaseIntf)srcParticipant ;
			
			srcParticipant.connect() ;
			
			partClient = srcParticipant;
			
			if (connected){
				SourceParticipant sp = (SourceParticipant)srcParticipant;
				mbf = sp.getMsgboardFactory();
			
				status = PS_CONNECTED ;
			}else{
				mbf = null;
				partClient = null;
				status = -1 ;
				System.out.println("Participant has not been created, application is not connected to the intended MessageBoard.");
			}
		}
		if (isReceptor()){
			 
			clearRemoteInstance();
			
			recParticipant = (ParticipantReceptorIntf) (getInstance());
			// instance = (ParticipantBaseIntf)recParticipant ;
			recParticipant.connect();
			partClient = recParticipant;
			
			ReceptorParticipant rp = (ReceptorParticipant)recParticipant;
			mbf = rp.getMsgboardFactory();
			
		}
		return partClient;
	}
	
	/** 
	 * just an abbreviation for accessing the distance control properties;</br>
	 * it returns a handle to the properties object, which is hosted by the MessageBaordFactory;</br></br>
	 * such it becomes accessible for the use layer instances
	 * 
	 * @return
	 */
	public DistanceControlProperties getDistanceCtrlProperties(){
		
		return getDistanceCtrlProperties(0);
	}
	/**
	 * 
	 * 
	 * @param mode ->  empty,0 = default object, 1=copy
	 * @return
	 */
	public DistanceControlProperties getDistanceCtrlProperties( int mode){
		
		DistanceControlProperties dcp0, dcp = null;
		
		if (mbf != null){
			
			dcp0 = mbf.getCompareKernel(0).getDistanceCtrl() ;
			
			if (mode>=1){
				dcp = new DistanceControlProperties(dcp0);
			}else{
				dcp=dcp0;
			}
		}

		return dcp;
	}
	
	
	
	// the long version is:
	// participant = (ParticipantSourceIntf)(factory.getInstance()) ;
	// participant.connect() ;

	
	public Object getInstance(){
		 
		String name;
		boolean hb = false;
		
		if (isSource()){
			
			if (instance==null){
				name = ParticipantSourceIntf.roleOfInstance.toLowerCase() + factoryProperties.getNameSuffix();
				SourceParticipant part = new SourceParticipant(this, factoryProperties.getConfigPath(), name);
				part.factory = this;

				part.setUseUplinkConnectPolling(false);

				instance = (ParticipantSourceIntf) (part);

				transferSettings(part);

				int gs = pSettings.getAlignmentGroupSize();
				factoryProperties.setSemiAlignedResultsBasketSize(gs);

				// part.setMessageBoardFactoryProperties(factoryProperties) ;

				// we create ... and introduce it to MessageBoardParticipant

				sourcesUpLinkService = new SourcesUpLinkService(this, part.out);// factoryProperties

				part.setUpLinkService((ParticipantUpLinkServiceIntf) sourcesUpLinkService);

				hb = true;
				if (factoryProperties.getUplinkPollingAllowedForAllProtocols() == false) {
					hb = factoryProperties.getProtocolId() <= MessageBoardFactoryProperties.protocolFTP; 
					// everything below  ftp, it  may  be activated for udp/tcp deliberately
				}
				if (factoryProperties.getProtocolId() >= MessageBoardFactoryProperties.protocolTCP) {
					// set interval a bit longer ...
					part.setUseUplinkConnectPollingPeriodLength(5000); 
					// actually, there is a direct connection, it's just a backup in order to lose  nothing
				}
				part.setUseUplinkConnectPolling(hb);
			}
		}
		
		if (isReceptor()){
			
			if (instance==null){
				
				name = ParticipantReceptorIntf.roleOfInstance.toLowerCase() + factoryProperties.getNameSuffix();
				
				ReceptorParticipant part = new ReceptorParticipant( this, factoryProperties.getConfigPath(),name);
				
				part.factory = this;
				instance = (ParticipantReceptorIntf)(part);
				
				transferSettings(part);
				// part.setMessageBoardFactoryProperties(factoryProperties) ;
				
				receptorsUpLinkService = new ReceptorsUpLinkService( this, part.out ) ;// factoryProperties
				
				part.setUpLinkService( (ParticipantUpLinkServiceIntf)receptorsUpLinkService  ) ;
				
				hb = factoryProperties.getProtocolId() <= 5; // everything below ftp, it may be activated for udp/tcp deliberately
				
				part.setUseUplinkConnectPolling( true ) ;
			}
			

		}
		
		if (isBoard()){ 
			
		}
		if (isBiRS()){  
			
		}
		
		return instance;
	}

	
	public long getInstanceID(){
		
		long iid = -1;
		if (xRegistration!=null){
			iid = xRegistration.getInstanceID() ;
		}
		return iid ;
	}
	 
	// TODO : TTL is so far not a part of the task package XML (age of expiry...)
	public long getTaskTTL(){
		return taskTTL;
	}
	public void setTaskTTL( int periodLen, String scale){
		 
		taskTTL = Age.getScaledTimeValue(periodLen, scale) ;
	}
	
	public TaskPackage getTaskPackage( String contentType  ){
							     // any string, e.g. "rawdata", "doc", "*") ;
		
		if (taskPackage == null){
			taskPackage = new TaskPackage( ); 
			taskPackage.setxRegistration(xRegistration);
		}

		taskPackage.setContentType( contentType ) ;
		
		return taskPackage ;
	}

	public int getStatus() {
		return status;
	}

	public MessageBoardFactory getMbf() {
		 
		return mbf;
	}
	
	public void setMessageBoardReference( MessageBoard mb){
		msgBoardReference = mb;
	}

	public MessageBoard getMsgBoardReference() {
		return msgBoardReference;
	}
	 

}


 